package com.vt.admin.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vt.admin.api.dto.OrganizationTree;
import com.vt.admin.api.dto.OrganizationTypeTree;
import com.vt.admin.api.dto.SysOrganizationListDto;
import com.vt.admin.api.dto.TreeNode;
import com.vt.admin.api.dto.user.redis.UserOrganization;
import com.vt.admin.api.entity.SysOrganization;
import com.vt.admin.api.entity.SysOrganizationType;
import com.vt.admin.api.vo.TreeUtil;
import com.vt.admin.exception.OrganizationTypeVerifyFailException;
import com.vt.admin.mapper.SysOrganizationMapper;
import com.vt.admin.mapper.SysOrganizationTypeMapper;
import com.vt.common.core.constant.CommonConstant;
import com.vt.common.core.util.R;
import com.vt.common.security.service.CustomUser;
import com.vt.common.security.util.OrganizationUtil;
import com.vt.common.security.util.SecurityUtils;
import com.vt.common.security.util.enums.DataScopeType;
import lombok.AllArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@AllArgsConstructor
public class SysOrganizationService implements CommandLineRunner {

    SysOrganizationMapper mapper;
    StringRedisTemplate redisTemplate;
    SysOrganizationTypeMapper typeMapper;
    OrganizationUtil organizationUtil;

    public R getById(String id) {
        return new R(mapper.selectById(id));
    }

    @Async
    public void writeRedis() {
        List<UserOrganization> list = mapper.selectUserOrganization();
        redisTemplate.opsForValue().set(CommonConstant.REDIS_ORGANIZATION_PREFIX, JSONUtil.toJsonStr(list));
    }

    @Override
    public void run(String... args) {
        writeRedis();
    }

    public List<OrganizationTree> tree() {
        List<OrganizationTree> list = mapper.queryByIdx(organizationUtil.children());

        String[] roots = organizationUtil.currUserOrganization();

        List<OrganizationTree> rs = new ArrayList();
        for (int i = 0, len = roots.length; i < len; i++) {
            rs.addAll(TreeUtil.buildByRecursive(list, roots[i]));
        }
        return rs;
        //return buildOrganizationTree(mapper.list(null));
    }

    public R<List<OrganizationTypeTree>> typeTree() {
        return new R(buildOrganizationTypeTree(mapper.typeList(null)));
    }

    public R<Boolean> typeSave(SysOrganizationType param) {
        String uId = SecurityUtils.getUser().getId();
        Date d = new Date();

        param.setUpdater(uId)
                .setUpdateTime(d)
                .setEditable(Integer.parseInt(CommonConstant.STATUS_NORMAL));

        if (StrUtil.isEmpty(param.getId())) {
            param.setCreater(uId)
                    .setCreateTime(d);
        }

        if (!verifyOrganizationType(null, param)) {
            throw new OrganizationTypeVerifyFailException();
        }
        return new R(param.insertOrUpdate());
    }

    public R<Boolean> save(SysOrganization organization) {
        String uId = SecurityUtils.getUser().getId();
        Date d = new Date();
        organization.setEditable(Integer.parseInt(CommonConstant.STATUS_NORMAL))
                .setUpdater(uId)
                .setUpdateTime(d)
                .setStatus(1);

        if (StrUtil.isEmpty(organization.getId())) {
            organization.setCreater(uId)
                    .setCreateTime(d);
        }

        if (!verifyOrganizationType(organization, null)) {
            throw new OrganizationTypeVerifyFailException();
        }

        organization.insertOrUpdate();
        writeRedis();
        return new R(Boolean.TRUE);
    }

    public R<SysOrganizationType> getTypeById(String typeId) {
        return new R(typeMapper.selectById(typeId));
    }

    private boolean verifyOrganizationType(List<SysOrganizationListDto> organizations, List<OrganizationTypeTree> types) {
        OrganizationTree tree = buildOrganizationTree(organizations).get(0);
        OrganizationTypeTree typeTree = buildOrganizationTypeTree(types).get(0);

        //为组织属性编号
        Map<String, Integer> typeNos = new HashMap();
        int start = 0;
        typeNos.put(typeTree.getId(), start);
        numberTypeTree(typeTree, typeNos, start);

        Integer[] flag = {0};
        verifyOrganizationType(tree, typeNos, flag);

        return flag[0] != -1;
    }

    private void numberTypeTree(TreeNode tree, Map<String, Integer> typeNos, Integer cNo) {
        if (CollUtil.isEmpty(tree.getChildren()))
            return;

        int no = cNo + 1;
        for (TreeNode node : tree.getChildren()) {

            typeNos.put(node.getId(), no);

            numberTypeTree(node, typeNos, no);
        }
    }

    private void verifyOrganizationType(TreeNode tree, Map<String, Integer> typeNos, Integer[] rsFlag) {
        if (rsFlag[0] == -1 || CollUtil.isEmpty(tree.getChildren()))
            return;

        for (TreeNode node : tree.getChildren()) {

            if (typeEquals((OrganizationTree) tree, (OrganizationTree) node, typeNos) < 0)
                rsFlag[0] = -1;

            verifyOrganizationType(node, typeNos, rsFlag);
        }
    }

    /**
     * 判断2个节点的组织属性层级
     *
     * @param node1
     * @param node2
     * @return < 0  = node1 < node2
     */
    private Integer typeEquals(OrganizationTree node1, OrganizationTree node2, Map<String, Integer> typeNos) {
        String t1 = node1.getType();
        String t2 = node2.getType();

        if (StrUtil.isEmpty(t1)
                || StrUtil.isEmpty(t2))
            return 0;

        if (!typeNos.containsKey(t1)
                || !typeNos.containsKey(t2))
            return 0;

        return typeNos.get(t2) - typeNos.get(t1);
    }


    private List<OrganizationTree> buildOrganizationTree(List<SysOrganizationListDto> list) {
        List<OrganizationTree> trees = new ArrayList();
        OrganizationTree node;
        for (SysOrganizationListDto item : list) {
            node = new OrganizationTree();
            node.setId(item.getId());
            node.setParentId(item.getParentId());
            node.setName(item.getName());
            node.setTypeText(item.getTypeText());
            node.setEditable(item.getEditable());
            node.setType(item.getType());
            trees.add(node);
        }
        return TreeUtil.buildByRecursive(trees, "1");
    }

    private List<OrganizationTypeTree> buildOrganizationTypeTree(List<OrganizationTypeTree> list) {
        List<OrganizationTypeTree> trees = new ArrayList();
        OrganizationTypeTree node;
        for (OrganizationTypeTree item : list) {
            node = new OrganizationTypeTree();
            node.setId(item.getId());
            node.setParentId(item.getParentId());
            node.setName(item.getName());
            node.setEditable(item.getEditable());
            trees.add(node);
        }
        return TreeUtil.buildByRecursiveParent(trees, "0");
    }

    /**
     * 组织 和 组织属性的完整性校验
     *
     * @return
     */
    private boolean verifyOrganizationType(SysOrganization organization, SysOrganizationType type) {
        List<SysOrganizationListDto> organizations = mapper.list(null);
        List<OrganizationTypeTree> types = mapper.typeList(null);

        if (null != organization) {
            for (SysOrganizationListDto obj : organizations) {
                if (StrUtil.equals(obj.getId(), organization.getId())) {
                    obj.setType(organization.getType());
                    obj.setParentId(organization.getParentId());
                }
            }
        }

        if (null != type) {
            for (OrganizationTypeTree obj : types) {
                if (StrUtil.equals(obj.getId(), type.getId())) {
                    obj.setParentId(type.getParentId());
                }
            }
        }

        return verifyOrganizationType(organizations, types);
    }
}
